﻿Imports System.Collections.ObjectModel
Imports System.Windows.Threading
Imports System.ComponentModel
Imports Co9320.NicoLiveService

Namespace ViewModel

    Public Class MainViewModel
        Inherits ViewModelBase

#Region "Properties"

        Private _liveProgramIdText As String = ""
        Public Property LiveProgramIdText() As String
            Get
                Return _liveProgramIdText
            End Get
            Set(ByVal value As String)
                _liveProgramIdText = value
                OnPropertyChanged("LiveProgramIdText")
            End Set
        End Property

        Protected ReadOnly Property LiveProgramId() As String
            Get
                Dim match = System.Text.RegularExpressions.Regex.Match(LiveProgramIdText, "(?<id>lv\d+)")
                If match.Success Then
                    Return match.Groups("id").Value
                Else
                    Return ""
                End If
            End Get
        End Property

        Private _cookieSource As CookieSource = Co9320.NicoLiveService.CookieSource.InternetExplorer
        Public Property CookieSource() As CookieSource
            Get
                Return _cookieSource
            End Get
            Set(ByVal value As CookieSource)
                _cookieSource = value
                OnPropertyChanged("CookieSource")
            End Set
        End Property

        Private _voteTitle As String = ""
        Public Property VoteTitle() As String
            Get
                Return _voteTitle
            End Get
            Set(ByVal value As String)
                _voteTitle = value
                OnPropertyChanged("VoteTitle")
            End Set
        End Property

        Private _addCommandString As String = "add"
        Public Property AddCommandString() As String
            Get
                Return _addCommandString
            End Get
            Set(ByVal value As String)
                _addCommandString = value
                OnPropertyChanged("AddCommandString")
            End Set
        End Property

        Private _votes As New ObservableCollection(Of VoteItemViewModel)
        Private ReadOnly Property Votes() As ObservableCollection(Of VoteItemViewModel)
            Get
                Return _votes
            End Get
        End Property

        Private _votesView As ICollectionView
        Public ReadOnly Property VotesView() As ICollectionView
            Get
                Return _votesView
            End Get
        End Property

        Private _ngUsers As New ObservableCollection(Of String)
        Public ReadOnly Property NgUsers() As ObservableCollection(Of String)
            Get
                Return _ngUsers
            End Get
        End Property

        Private _autoVoteItemAdding As Boolean
        Public Property AutoVoteItemAdding() As Boolean
            Get
                Return _autoVoteItemAdding
            End Get
            Set(ByVal value As Boolean)
                _autoVoteItemAdding = value
                OnPropertyChanged("AutoVoteItemAdding")
            End Set
        End Property

        Private _allowCasterCommentOnly As Boolean
        Public Property AllowCasterCommentOnly() As Boolean
            Get
                Return _allowCasterCommentOnly
            End Get
            Set(ByVal value As Boolean)
                _allowCasterCommentOnly = value
                OnPropertyChanged("AllowCasterCommentOnly")
            End Set
        End Property

        Private _allowPluralVoting As Boolean
        Public Property AllowPluralVoting() As Boolean
            Get
                Return _allowPluralVoting
            End Get
            Set(ByVal value As Boolean)
                _allowPluralVoting = value
                OnPropertyChanged("AllowPluralVoting")
            End Set
        End Property

        Private _allowVotingByAnonymityOnly As Boolean
        Public Property AllowVotingByAnonymityOnly() As Boolean
            Get
                Return _allowVotingByAnonymityOnly
            End Get
            Set(ByVal value As Boolean)
                _allowVotingByAnonymityOnly = value
                OnPropertyChanged("AllowVotingByAnonymityOnly")
            End Set
        End Property

        Private _comments As New ObservableCollection(Of Comment)
        Private ReadOnly Property Comments() As ObservableCollection(Of Comment)
            Get
                Return _comments
            End Get
        End Property

        Private WithEvents _commentsView As ICollectionView
        Public ReadOnly Property CommentsView() As ICollectionView
            Get
                Return _commentsView
            End Get
        End Property

        Private _statusMessage As String
        Public Property StatusMessage() As String
            Get
                Return _statusMessage
            End Get
            Set(ByVal value As String)
                _statusMessage = value
                OnPropertyChanged("StatusMessage")
            End Set
        End Property

#End Region


        Public Sub New()
            _votesView = CollectionViewSource.GetDefaultView(Votes)
            _votesView.SortDescriptions.Clear()
            _votesView.SortDescriptions.Add(New SortDescription("Count", ListSortDirection.Descending))

            _commentsView = CollectionViewSource.GetDefaultView(Comments)
            _commentsView.SortDescriptions.Clear()
            _commentsView.SortDescriptions.Add(New SortDescription("No", ListSortDirection.Ascending))
        End Sub

        Private WithEvents CommentServer As CommentServer

#Region "ConnectCommand"

        Private _connectCommand As ICommand
        Public ReadOnly Property ConnectCommand() As ICommand
            Get
                If _connectCommand Is Nothing Then
                    _connectCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.Connect), New Predicate(Of Object)(AddressOf CanConnect))
                End If
                Return _connectCommand
            End Get
        End Property

        Private Sub Connect(ByVal obj As Object)
            Try
                If CommentServer IsNot Nothing Then
                    CommentServer.DisconnectAsync()
                End If

                CommentServer = New CommentServer
                CommentServer.ConnectAsync(LiveProgramId, CookieSource)
            Catch ex As Exception
            End Try
        End Sub

        Private Function CanConnect(ByVal obj As Object) As Boolean
            If LiveProgramId = "" Then
                Return False
            End If

            If CommentServer Is Nothing OrElse _
               CommentServer.Status = ConnectionStatus.Disconnected Then
                Return True
            End If
            Return False
        End Function

#End Region

#Region "DisconnectCommand"

        Private _disconnectCommand As ICommand
        Public ReadOnly Property DisconnectCommand() As ICommand
            Get
                If _disconnectCommand Is Nothing Then
                    _disconnectCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.Disconnect), New Predicate(Of Object)(AddressOf CanDisconnect))
                End If
                Return _disconnectCommand
            End Get
        End Property

        Private Sub Disconnect(ByVal obj As Object)
            If CommentServer IsNot Nothing Then
                CommentServer.DisconnectAsync()
            End If
        End Sub

        Private Function CanDisconnect(ByVal obj As Object) As Boolean
            If CommentServer IsNot Nothing AndAlso CommentServer.Status = ConnectionStatus.Connected Then
                Return True
            End If
            Return False
        End Function

#End Region

        Private Sub NicoLiveService_CommentReceived(ByVal sender As Object, ByVal e As CommentEventArgs) Handles CommentServer.CommentReceived
            Console.WriteLine(e.Comment.Text)

            If Not IsStarted Then
                Exit Sub
            End If

            If e.Comment.IsNG Then
                Exit Sub
            End If

            If AllowVotingByAnonymityOnly AndAlso Not e.Comment.IsAnonymity Then
                Exit Sub
            End If

            If NgUsers.Contains(e.Comment.UserId) Then
                Exit Sub
            End If

            If e.Comment.Text.StartsWith("/") Then
                ' 生放送コマンド
                Exit Sub
            End If


            Const replaceOldChars As String = "０１２３４５６７８９"
            Const replaceNewChars As String = "0123456789"

            Dim message = e.Comment.Text
            For i = 0 To replaceOldChars.Length - 1
                message = message.Replace(replaceOldChars(i), replaceNewChars(i))
            Next
            message = message.Trim

            ' 追加コマンドと項目分離、追加コマンド判定
            Dim isAddCommand As Boolean = False
            If AddCommandString.Trim <> "" Then
                Dim command = AddCommandString.Trim.ToUpperInvariant
                If message.ToUpperInvariant.StartsWith(command) Then
                    isAddCommand = True
                    message = message.Trim.Substring(command.Trim.Length).Trim
                End If
            Else
                isAddCommand = True
            End If

            If message = "" Then
                ' 空文字
                Exit Sub
            End If


            ' 投票判定
            Dim voteNo As Integer

            ' MEMO message はコマンドが削除された文字列 add 123 の場合 123版への投票になる
            Dim match = System.Text.RegularExpressions.Regex.Match(message, "^(?<no>\d+)$")
            If match.Success Then
                Try
                    Dim no = CInt(match.Groups("no").Value)
                    Dim votes = From v In Me.Votes Where v.No = no
                    If votes.Count > 0 Then
                        voteNo = votes.SingleOrDefault.No
                    End If
                Catch ex As OverflowException
                    voteNo = 0
                End Try
            Else
                ' TODO 二重に項目追加されている場合がある
                Dim votes = From v In Me.Votes Where v.Text = message
                If votes.Count > 0 Then
                    voteNo = votes.SingleOrDefault.No
                End If
            End If

            If voteNo > 0 Then
                ' 投票

                ' 票削除(票移動)
                If Not AllowPluralVoting Then
                    For Each v In Me.Votes
                        v.Decrement(e.Comment)
                    Next
                End If

                Dim votes = From v In Me.Votes Where v.No = voteNo
                votes.SingleOrDefault.Increment(e.Comment)

            ElseIf match.Success Then
                Exit Sub

            Else


                ' 新規項目追加
                Me.Comments.Add(e.Comment)

                If AutoVoteItemAdding AndAlso isAddCommand Then
                    If AllowCasterCommentOnly AndAlso e.Comment.Premium <> 3 Then
                        Exit Sub
                    End If

                    AddVoteItem(e.Comment)
                End If
            End If


            VotesView.Refresh()
            OnPropertyChanged("VotesView")

        End Sub

        Private Sub AddVoteItem(ByVal comment As Comment, Optional ByVal andVote As Boolean = True)

            Dim voteItem As VoteItemViewModel
            If AllowPluralVoting Then
                voteItem = New VoteItemViewModel(comment, comment.Premium <> 3)
            Else
                Dim usersVotes = From v In Me.Votes Where v.ContainUser(comment.UserId)
                If usersVotes.Count > 0 Then
                    ' 他の項目に投票している場合、項目のみ追加
                    voteItem = New VoteItemViewModel(comment, False)
                Else
                    voteItem = New VoteItemViewModel(comment, comment.Premium <> 3)
                End If
            End If

            voteItem.DeleteCommandString(AddCommandString)
            Votes.Add(voteItem)

        End Sub

        Private Sub Decrement(ByVal comment As Comment)
            For Each v In Votes
                v.Decrement(comment)
            Next
        End Sub

        ' for debug
        Public Sub InjectComment(ByVal comment As Comment)
            NicoLiveService_CommentReceived(Me, New CommentEventArgs(comment))
        End Sub


        Private IsStarted As Boolean

#Region "StartVoteCommand"

        Private _startVoteCommand As ICommand
        Public ReadOnly Property StartVoteCommand() As ICommand
            Get
                If _startVoteCommand Is Nothing Then
                    _startVoteCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.StartVote), New Predicate(Of Object)(AddressOf CanStartVote))
                End If
                Return _startVoteCommand
            End Get
        End Property

        Private Sub StartVote(ByVal obj As Object)
            IsStarted = True
        End Sub

        Private Function CanStartVote(ByVal obj As Object) As Boolean
            Return Not IsStarted
        End Function

#End Region

#Region "StopVoteCommand"

        Private _stopVoteCommand As ICommand
        Public ReadOnly Property StopVoteCommand() As ICommand
            Get
                If _stopVoteCommand Is Nothing Then
                    _stopVoteCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.StopVote), New Predicate(Of Object)(AddressOf CanStopVote))
                End If
                Return _stopVoteCommand
            End Get
        End Property

        Private Sub StopVote(ByVal obj As Object)
            IsStarted = False
        End Sub

        Private Function CanStopVote(ByVal obj As Object) As Boolean
            Return IsStarted
        End Function

#End Region

#Region "SelectDisplayAreaCommand"

        Public Event RequestAreaSelection As EventHandler(Of EventArgs)
        Protected Overridable Sub OnSelectDisplayArea(ByVal e As EventArgs)
            RaiseEvent RequestAreaSelection(Me, e)
        End Sub

        Private _selectDisplayAreaCommand As ICommand
        Public ReadOnly Property SelectDisplayAreaCommand() As ICommand
            Get
                If _selectDisplayAreaCommand Is Nothing Then
                    _selectDisplayAreaCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.SelectDisplayArea))
                End If
                Return _selectDisplayAreaCommand
            End Get
        End Property

        Private Sub SelectDisplayArea(ByVal obj As Object)
            OnSelectDisplayArea(New EventArgs)
        End Sub

#End Region

#Region "SelectFontCommand"

        Public Event RequestFontSelection As EventHandler(Of EventArgs)
        Protected Overridable Sub OnSelectFont(ByVal e As EventArgs)
            RaiseEvent RequestFontSelection(Me, e)
        End Sub

        Private _selectFontCommand As ICommand
        Public ReadOnly Property SelectFontCommand() As ICommand
            Get
                If _selectFontCommand Is Nothing Then
                    _selectFontCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.SelectFont))
                End If
                Return _selectFontCommand
            End Get
        End Property

        Private Sub SelectFont(ByVal obj As Object)
            OnSelectFont(New EventArgs)
        End Sub

#End Region

#Region "AddVoteItemCommand"

        Private _addVoteItemCommand As ICommand
        Public ReadOnly Property AddVoteItemCommand() As ICommand
            Get
                If _addVoteItemCommand Is Nothing Then
                    _addVoteItemCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.AddVoteItem), New Predicate(Of Object)(AddressOf CanAddVoteItem))
                End If
                Return _addVoteItemCommand
            End Get
        End Property

        Private Sub AddVoteItem(ByVal obj As Object)
            Dim comment = DirectCast(CommentsView.CurrentItem, Comment)
            AddVoteItem(comment)
        End Sub

        Private Function CanAddVoteItem(ByVal obj As Object) As Boolean
            If CommentsView.CurrentItem Is Nothing Then
                Return False
            End If
            Dim comment = DirectCast(CommentsView.CurrentItem, Comment)
            Dim removeItem = From v In Votes Where v.No = comment.No
            Return removeItem.Count = 0
        End Function

#End Region

#Region "DeleteVoteItemCommand"

        Private _deleteVoteItemCommand As ICommand
        Public ReadOnly Property DeleteVoteItemCommand() As ICommand
            Get
                If _deleteVoteItemCommand Is Nothing Then
                    _deleteVoteItemCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.DeleteVoteItem), New Predicate(Of Object)(AddressOf CanDeleteVoteItem))
                End If
                Return _deleteVoteItemCommand
            End Get
        End Property

        Private Sub DeleteVoteItem(ByVal obj As Object)
            Dim comment = DirectCast(CommentsView.CurrentItem, Comment)
            Dim removeItem = From v In Votes Where v.No = comment.No
            Votes.Remove(removeItem.SingleOrDefault)
        End Sub

        Private Function CanDeleteVoteItem(ByVal obj As Object) As Boolean
            If CommentsView.CurrentItem Is Nothing Then
                Return False
            End If
            Dim comment = DirectCast(CommentsView.CurrentItem, Comment)
            Dim removeItem = From v In Votes Where v.No = comment.No
            Return removeItem.Count > 0
        End Function

#End Region

#Region "DeleteAllVoteItemsCommand"

        Private _deleteAllVoteItemsCommand As ICommand
        Public ReadOnly Property DeleteAllVoteItemsCommand() As ICommand
            Get
                If _deleteAllVoteItemsCommand Is Nothing Then
                    _deleteAllVoteItemsCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.DeleteAllVoteItems), New Predicate(Of Object)(AddressOf CanDeleteAllVoteItems))
                End If
                Return _deleteAllVoteItemsCommand
            End Get
        End Property

        Private Sub DeleteAllVoteItems(ByVal obj As Object)
            Votes.Clear()
        End Sub

        Private Function CanDeleteAllVoteItems(ByVal obj As Object) As Boolean
            Return Votes.Count > 0
        End Function

#End Region

#Region "ResetVoteCountCommand"

        Private _resetVoteCountCommand As ICommand
        Public ReadOnly Property ResetVoteCountCommand() As ICommand
            Get
                If _resetVoteCountCommand Is Nothing Then
                    _resetVoteCountCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.ResetVoteCount), New Predicate(Of Object)(AddressOf CanResetVoteCount))
                End If
                Return _resetVoteCountCommand
            End Get
        End Property

        Private Sub ResetVoteCount(ByVal obj As Object)
            For Each v In Votes
                v.Clear()
            Next
        End Sub

        Private Function CanResetVoteCount(ByVal obj As Object) As Boolean
            Return Votes.Count > 0
        End Function

#End Region

#Region "AddNgUserCommand"

        Private _addNgUserCommand As ICommand
        Public ReadOnly Property AddNgUserCommand() As ICommand
            Get
                If _addNgUserCommand Is Nothing Then
                    _addNgUserCommand = New RelayCommand(New Action(Of Object)(AddressOf Me.AddNgUser), New Predicate(Of Object)(AddressOf CanAddNgUser))
                End If
                Return _addNgUserCommand
            End Get
        End Property

        Private Sub AddNgUser(ByVal obj As Object)
            Dim comment = DirectCast(CommentsView.CurrentItem, Comment)
            If Not _ngUsers.Contains(comment.UserId) Then
                _ngUsers.Add(comment.UserId)
            End If
        End Sub

        Private Function CanAddNgUser(ByVal obj As Object) As Boolean
            If CommentsView.CurrentItem Is Nothing Then
                Return False
            End If
            Dim comment = DirectCast(CommentsView.CurrentItem, Comment)
            If _ngUsers.Contains(comment.UserId) Then
                Return False
            End If
            Return True
        End Function

#End Region



        Private Sub CommentServer_ConfiguringWordResponseReceived(ByVal sender As Object, ByVal e As Co9320.NicoLiveService.ConfiguringWordResponseEventArgs) Handles CommentServer.ConfiguringWordResponseReceived
            If e.Respone.HasError Then

            End If
        End Sub

        Private Sub CommentServer_ConnectionStatusChanged(ByVal sender As Object, ByVal e As Co9320.NicoLiveService.ConnectionEventArgs) Handles CommentServer.ConnectionStatusChanged
            Select Case e.Status
                Case ConnectionStatus.Connecting
                    Me.StatusMessage = "接続中"
                Case ConnectionStatus.Connected
                    Me.StatusMessage = "接続しました"
                Case ConnectionStatus.Disconnecting
                    Me.StatusMessage = "切断中"
                Case ConnectionStatus.Disconnected
                    Me.StatusMessage = "切断しました"
            End Select
        End Sub

        Private Sub CommentServer_PlayerStatusReceived(ByVal sender As Object, ByVal e As Co9320.NicoLiveService.PlayerStatusEventArgs) Handles CommentServer.PlayerStatusReceived

        End Sub

    End Class

End Namespace
